#pragma once
#include "requestor.hpp"
#include <unordered_map>

namespace jsonrpc
{
    //  Provider +  MethodHost(实现负载均衡, 在套一层) + Discoverer
    namespace client
    {
        class Provider
        {
        public:
            using ptr = std::shared_ptr<Provider>;
            Provider(const Requestor::ptr &requestor) : _requestor(requestor) {}
        public:
            // 1.注册服务(构建请求 + 发送)
            bool registryMethod(const BaseConnection::ptr &conn, const std::string &method, const Address &host)
            {
                auto msg_req = MessageFactory::create<ServiceRequest>();
                msg_req->setId(UUID::uuid());
                msg_req->setMType(MType::REQ_SERVICE);
                msg_req->setMethod(method);
                msg_req->setHost(host);
                msg_req->setOptype(ServiceOptype::SERVICE_REGISTRY);

                // 注册后就可以收到对应的响应 
                BaseMessage::ptr msg_rsp;
                bool ret = _requestor->send(conn, msg_req, msg_rsp); // 同步调用
                if(ret == false)
                {
                    ELOG("%s 服务注册失败！", method.c_str());
                    return false;
                }

                auto service_rsp = std::dynamic_pointer_cast<ServiceResponse>(msg_rsp);
                if(service_rsp.get() == nullptr)
                {
                    ELOG("响应类型向下转换失败！");
                    return false;
                }

                if(service_rsp->rcode() != RCode::RCODE_OK)
                {
                    ELOG("服务注册失败，原因：%s", errReason(service_rsp->rcode()).c_str());
                    return false;
                }
                return true;
            }
            // 2.向注册中心发送心跳包
            void sendHeartbeat(const BaseConnection::ptr &conn, const BaseMessage::ptr& msg)
            {
                client::Requestor::AsyncResponse rsp_future;
                bool ret = _requestor->send(conn, msg, rsp_future);
                // 异步-- 向注册中心发送的心跳包, 不需要获取结果
            }
        private:
            Requestor::ptr _requestor;
        };

        class MethodHost
        {
        public:
            using ptr = std::shared_ptr<MethodHost>;
            MethodHost() : _idx(0) {}
            MethodHost(const std::vector<Address> &hosts): _idx(0), _hosts(hosts.begin(), hosts.end())
            {}

        public:
            // 1.添加
            void appendHost(const Address &host)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _hosts.push_back(host);
            }

            // 2.删除
            void removeHost(const Address &host)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                for(auto it = _hosts.begin(); it != _hosts.end(); it++)
                {
                    if(*it == host){
                        _hosts.erase(it);
                        break;
                    }
                }
            }

            // 3.随机选着一个服务节点(负载均衡策略)
            Address chooseHost()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                size_t pos = (_idx++) % _hosts.size();
                return _hosts[pos];
            }

            // 4.是否为空
            bool empty()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                return _hosts.empty();
            }
        private:
            std::mutex _mutex;
            size_t _idx; // 记录当前选到那一个主机了
            std::vector<Address> _hosts; // 记录提供该服务的主机信息
        };

        class Discoverer
        {
        public:
            using OfflineCallback = std::function<void(const Address &)>;
            using ptr = std::shared_ptr<Discoverer>;
            Discoverer(const Requestor::ptr& requestor, const OfflineCallback& cb)
            :_requestor(requestor)
            ,_offline_callback(cb)
            {}

        public:
            // 发现服务
            bool serviceDiscovery(const BaseConnection::ptr &conn, const std::string &method, Address &host)
            {
                // 1.先看连接池里面有没有能直接提供服务的主机 -- 尝试复用连接
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _method_hosts.find(method);
                    if(it != _method_hosts.end())
                    {
                        host = it->second->chooseHost();
                        return true;
                    }
                }
                // 不存在提供服务的主机, 尝试发现服务
                auto msg_req = MessageFactory::create<ServiceRequest>();
                msg_req->setId(UUID::uuid());
                msg_req->setMethod(method);
                msg_req->setMType(MType::REQ_SERVICE);
                msg_req->setOptype(ServiceOptype::SERVICE_DISCOVERY);
                BaseMessage::ptr msg_rsp; // 输出型参数 -- 回去拿响应结果(返回提供服务的主机地址)
                bool ret = _requestor->send(conn, msg_req, msg_rsp);
                if(ret == false)
                {
                    ELOG("服务发现失败！");
                    return false;
                }
                auto service_rsp = std::dynamic_pointer_cast<ServiceResponse>(msg_rsp);
                if (!service_rsp)
                {
                    ELOG("服务发现失败！响应类型转换失败！");
                    return false;
                }
                if (service_rsp->rcode() != RCode::RCODE_OK)
                {
                    ELOG("服务发现失败！%s", errReason(service_rsp->rcode()).c_str());
                    return false;
                }
                // 更新信息
                // 当前服务没有主机提供, 我们重新发现了服务 -- 更新映射关系
                std::unique_lock<std::mutex> lock(_mutex);
                auto method_hosts = std::make_shared<MethodHost>(service_rsp->hosts()); // 迭代器区间构造, 拿出提供该服务的所有主机信息
                if(method_hosts->empty())
                {
                    ELOG("%s 服务发现失败！没有能够提供服务的主机！", method.c_str());
                    return false;
                }
                host = method_hosts->chooseHost(); // 负载均衡
                _method_hosts[method] = method_hosts; //覆盖之前服务的地址信息
                return true;
            }

            // 注册给dispatcher处理服务上线/下线的回调函数(处理上线/下线通知消息)
            void onServiceRequest(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg)
            {
                // 1.判断服务类型(根据类型进行处理)
                auto optype = msg->optype();
                std::string method = msg->method();
                std::unique_lock<std::mutex> lock(_mutex);
                // 服务上线
                if(optype == ServiceOptype::SERVICE_ONLINE)
                {
                    auto it = _method_hosts.find(method);
                    if(it == _method_hosts.end())
                    {
                        // 新服务上线
                        auto method_host = std::make_shared<MethodHost>();
                        method_host->appendHost(msg->host());
                        _method_hosts[method] = method_host;
                    }
                    else
                        it->second->appendHost(msg->host());
                }

                // 服务下线
                if(optype == ServiceOptype::SERVICE_OFFLINE)
                {
                    auto it = _method_hosts.find(method);
                    if(it == _method_hosts.end()) return;
                    else
                    {
                        it->second->removeHost(msg->host());
                        _offline_callback(msg->host()); //服务下线通知, 删除池子中的连接对象
                    }
                }
            }
        private:
            std::mutex _mutex;
            Requestor::ptr _requestor;
            OfflineCallback _offline_callback;
            std::unordered_map<std::string, MethodHost::ptr> _method_hosts; // 连接池
        };
    }
}